﻿using KGySoft.Drawing;
using KGySoft.Drawing.Imaging;
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using ttbit.form;

namespace bin.imgconv
{
    internal class ImgConv
    {
        public static ttbit.form.ImgData ConvertTexture(StbImageSharp.ImageResult srcimg, TTIMGFormat format, StbImageSharp.ImageResult palimg)
        {
            ttbit.form.ImgData img = new ttbit.form.ImgData();
            img.format = format;
            img.pals = new List<ttbit.form.PalData>();
            img.width = srcimg.Width;
            img.height = srcimg.Height;

            Dictionary<int, byte> palconv = null;
            bool needpal = (format == TTIMGFormat.PAL8 || format == TTIMGFormat.P5A3);
            if (needpal)
            {
                try
                {
                    palconv = GenPal(srcimg, format, palimg, img);
                    //如果不能抽出完整色表，就需要做颜色量化。
                }
                catch (Exception err)
                {
                    if (format == TTIMGFormat.PAL8)
                    {
                        SlowConvPal255(srcimg, img);
                        return img;
                    }
                    if (format == TTIMGFormat.P5A3)
                    {
                        SlowConvP5A3(srcimg, img);
                        return img;
                    }
                }
            }
            if (format == TTIMGFormat.RGBA)
            {
                img.pixel = new byte[img.width * img.height * 4];
                for (var y = 0; y < img.height; y++)
                {
                    for (var x = 0; x < img.width; x++)
                    {
                        img.pixel[(y * img.width + x) * 4 + 0] = srcimg.Data[(y * img.width + x) * 4 + 0];
                        img.pixel[(y * img.width + x) * 4 + 1] = srcimg.Data[(y * img.width + x) * 4 + 1];
                        img.pixel[(y * img.width + x) * 4 + 2] = srcimg.Data[(y * img.width + x) * 4 + 2];
                        img.pixel[(y * img.width + x) * 4 + 3] = srcimg.Data[(y * img.width + x) * 4 + 3];
                    }
                }
                throw new Exception("easy,but not support for now.");
            }
            else if (format == TTIMGFormat.GRAY)
            {
                img.pixel = new byte[img.width * img.height];
                for (var y = 0; y < img.height; y++)
                {
                    for (var x = 0; x < img.width; x++)
                    {
                        var r = (float)srcimg.Data[(y * img.width + x) * 4 + 0] / 255.0f;
                        var g = (float)srcimg.Data[(y * img.width + x) * 4 + 1] / 255.0f;
                        var b = (float)srcimg.Data[(y * img.width + x) * 4 + 2] / 255.0f;
                        var a = srcimg.Data[(y * img.width + x) * 4 + 3];
                        if (a < 128)
                        {
                            img.pixel[y * img.width + x] = 0;
                        }
                        else
                        {
                            var gray = (r * 0.299f + g * 0.587f + b * 0.114f) * 255.0f;
                            img.pixel[y * img.width + x] = (byte)gray;
                        }
                    }
                }
            }
            else if (format == TTIMGFormat.PAL8)
            {
                //这是完美转换表，如果不完美，可能需要弄一个lut转换表
                try
                {
                    img.pixel = new byte[img.width * img.height];
                    for (var y = 0; y < img.height; y++)
                    {
                        for (var x = 0; x < img.width; x++)
                        {
                            var r = srcimg.Data[(y * img.width + x) * 4 + 0];
                            var g = srcimg.Data[(y * img.width + x) * 4 + 1];
                            var b = srcimg.Data[(y * img.width + x) * 4 + 2];
                            var a = srcimg.Data[(y * img.width + x) * 4 + 3];
                            if (a < 128)
                            {
                                img.pixel[y * img.width + x] = 0;
                            }
                            else
                            {
                                int addr = r << 16 | g << 8 | b;
                                img.pixel[y * img.width + x] = palconv[addr];
                            }
                        }
                    }
                }
                catch
                {
                    Console.WriteLine("Todo:通过LUT转换表");
                    //byte[] lutgray = new byte[65536];
                    //byte[] lutgrba = new byte[65536 * 4];
                    //lut.CalcLUT(img.pals[0], 1, lutgrba, lutgray, false, true);

                    Color[] pal = new Color[255];
                    for (var i = 0; i < 255; i++)
                    {
                        var r = img.pals[0].paldata[(i + 1) * 4 + 0];
                        var g = img.pals[0].paldata[(i + 1) * 4 + 1];
                        var b = img.pals[0].paldata[(i + 1) * 4 + 2];
                        var a = 255;// img.pals[0].paldata[(i + 1) * 4 + 3];
                        pal[i] = Color.FromArgb(a, r, g, b);
                    }

                    IQuantizer quantizer = PredefinedColorsQuantizer.FromCustomPalette(pal, Color.Transparent, 128);
                    System.Drawing.Bitmap bmp = Img2Bmp(srcimg);
                    //bmp.Save("_t1.png");
                    var bmp2 = bmp.ConvertPixelFormat(System.Drawing.Imaging.PixelFormat.Format8bppIndexed, quantizer);
                    //bmp2.Save("_tst.png");

                    img.pixel = new byte[img.width * img.height];
                    var data = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, img.width, img.height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                    unsafe
                    {
                        byte* ptr = (byte*)data.Scan0;
                        for (var y = 0; y < srcimg.Height; y++)
                        {
                            for (var x = 0; x < srcimg.Width; x++)
                            {

                                var index = ptr[y * data.Stride + x];
                                //if (index == 255) index = 0;
                                //else
                                //    index++;
                                img.pixel[(y * srcimg.Width + x)] = (byte)(index + 1);

                            }
                        }
                    }
                }
            }
            else if (format == TTIMGFormat.P5A3)
            {
                //这是完美转换表，不完美，可能需要弄一个lut转换表
                try
                {
                    img.pixel = new byte[img.width * img.height];
                    for (var y = 0; y < img.height; y++)
                    {
                        for (var x = 0; x < img.width; x++)
                        {
                            var r = srcimg.Data[(y * img.width + x) * 4 + 0];
                            var g = srcimg.Data[(y * img.width + x) * 4 + 1];
                            var b = srcimg.Data[(y * img.width + x) * 4 + 2];
                            var a = srcimg.Data[(y * img.width + x) * 4 + 3];

                            {
                                int addr = r << 16 | g << 8 | b;
                                var index = palconv[addr];
                                var color = (index & 0x1f) | (a & 0xE0); //alpha 要高三位 ， index 要低五位 ,乘法表示为 int(alpha /32)*32+ (index%32)
                                img.pixel[y * img.width + x] = (byte)color;
                            }
                        }
                    }
                }
                catch
                {
                    Console.WriteLine("Todo:通过LUT转换表");
                    //byte[] lutgray = new byte[65536];
                    //byte[] lutgrba = new byte[65536 * 4];
                    //lut.CalcLUT(img.pals[0], 1, lutgrba, lutgray, false, true);

                    Color[] pal = new Color[32];
                    for (var i = 0; i < 32; i++)
                    {
                        var r = img.pals[0].paldata[(i) * 4 + 0];
                        var g = img.pals[0].paldata[(i) * 4 + 1];
                        var b = img.pals[0].paldata[(i) * 4 + 2];
                        var a = 255;// img.pals[0].paldata[(i) * 4 + 3];
                        pal[i] = Color.FromArgb(a, r, g, b);
                    }

                    IQuantizer quantizer = PredefinedColorsQuantizer.FromCustomPalette(pal, Color.Transparent, 1);
                    System.Drawing.Bitmap bmp = Img2Bmp(srcimg);
                    //bmp.Save("_t1.png");
                    var bmp2 = bmp.ConvertPixelFormat(System.Drawing.Imaging.PixelFormat.Format8bppIndexed, quantizer);
                    //bmp2.Save("_tst.png");

                    img.pixel = new byte[img.width * img.height];
                    var data = bmp2.LockBits(new System.Drawing.Rectangle(0, 0, img.width, img.height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
                    unsafe
                    {
                        byte* ptr = (byte*)data.Scan0;
                        for (var y = 0; y < srcimg.Height; y++)
                        {
                            for (var x = 0; x < srcimg.Width; x++)
                            {

                                var index = ptr[y * data.Stride + x];
                                if (index > 31)
                                    throw new Exception("error index");
                                var a = srcimg.Data[(y * img.width + x) * 4 + 3];

                                var color = (index & 0x1f) | (a & 0xE0);
                                //if (index == 255) index = 0;
                                //else
                                //    index++;
                                img.pixel[(y * srcimg.Width + x)] = (byte)color;

                            }
                        }
                    }
                }
            }
            return img;
        }
        static void QuickSave(string file, byte[] data, int width, int height, bool gray)
        {
            StbImageWriteSharp.ImageWriter w = new StbImageWriteSharp.ImageWriter();
            using var fs = System.IO.File.OpenWrite(file);
            w.WritePng(data, width, height, gray ? StbImageWriteSharp.ColorComponents.Grey : StbImageWriteSharp.ColorComponents.RedGreenBlueAlpha, fs);
        }
        static Bitmap Img2Bmp(StbImageSharp.ImageResult srcimg)
        {
            System.Drawing.Bitmap bmp = new System.Drawing.Bitmap(srcimg.Width, srcimg.Height, System.Drawing.Imaging.PixelFormat.Format32bppArgb);
            for (var y = 0; y < srcimg.Height; y++)
            {
                for (var x = 0; x < srcimg.Width; x++)
                {
                    var r = srcimg.Data[(y * srcimg.Width + x) * 4 + 0];
                    var g = srcimg.Data[(y * srcimg.Width + x) * 4 + 1];
                    var b = srcimg.Data[(y * srcimg.Width + x) * 4 + 2];
                    var a = srcimg.Data[(y * srcimg.Width + x) * 4 + 3];
                    bmp.SetPixel(x, y, System.Drawing.Color.FromArgb(a, r, g, b));
                    //{
                    //    int addr = r << 16 | g << 8 | b;
                    //    var index = palconv[addr];
                    //    var color = (index & 0x1f) | (a & 0xE0); //alpha 要高三位 ， index 要低五位 ,乘法表示为 int(alpha /32)*32+ (index%32)
                    //    img.pixel[y * img.width + x] = (byte)color;
                    //}
                }
            }
            return bmp;
        }
        static void SlowConvPal255(StbImageSharp.ImageResult srcimg, ImgData output)
        {
            Console.WriteLine("slowpal256");
            System.Drawing.Bitmap bmp = Img2Bmp(srcimg);

            nQuant.WuQuantizer quantizer = new nQuant.WuQuantizer();
            var outbmp = quantizer.QuantizeImage(bmp, 127, 255, null, 256) as Bitmap;
            var c = outbmp.Palette.Entries[255];
            if (c.A != 0)
            {
                throw new Exception("no tran color.");
            }
            output.pals = new List<PalData>();
            var pal = new PalData();
            pal.colorcount = 255;
            pal.paldata = new byte[256 * 4];
            Dictionary<int, byte> palconv = new Dictionary<int, byte>();
            for (var i = 0; i < 255; i++)
            {
                var r = outbmp.Palette.Entries[i].R;
                var g = outbmp.Palette.Entries[i].G;
                var b = outbmp.Palette.Entries[i].B;
                pal.paldata[(i + 1) * 4 + 0] = outbmp.Palette.Entries[i].R;
                pal.paldata[(i + 1) * 4 + 1] = outbmp.Palette.Entries[i].G;
                pal.paldata[(i + 1) * 4 + 2] = outbmp.Palette.Entries[i].B;
                pal.paldata[(i + 1) * 4 + 3] = outbmp.Palette.Entries[i].A;
                int addr = r << 16 | g << 8 | b;
                palconv[addr] = (byte)(i + 1);
            }
            output.pals.Add(pal);

            output.width = outbmp.Width;
            output.height = outbmp.Height;
            output.pixel = new byte[output.width * output.height];
            var data = outbmp.LockBits(new System.Drawing.Rectangle(0, 0, outbmp.Width, outbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            unsafe
            {
                byte* ptr = (byte*)data.Scan0;
                for (var y = 0; y < srcimg.Height; y++)
                {
                    for (var x = 0; x < srcimg.Width; x++)
                    {

                        var index = ptr[y * data.Stride + x];
                        if (index == 255) index = 0;
                        else
                            index++;
                        output.pixel[(y * srcimg.Width + x)] = index;

                    }
                }
            }
        }
        static void SlowConvP5A3(StbImageSharp.ImageResult srcimg, ImgData output)
        {
            Console.WriteLine("SlowConvP5A3");
            System.Drawing.Bitmap bmp = Img2Bmp(srcimg);
            nQuant.WuQuantizer quantizer = new nQuant.WuQuantizer();
            var outbmp = quantizer.QuantizeImage(bmp, 1, 1, null, 32) as Bitmap;
            //var c = outbmp.Palette.Entries[255];
            //if (c.A != 0)
            //{
            //    throw new Exception("no tran color.");
            //}
            output.pals = new List<PalData>();
            var pal = new PalData();
            pal.colorcount = 32;
            pal.paldata = new byte[32 * 4];
            Dictionary<int, byte> palconv = new Dictionary<int, byte>();
            for (var i = 0; i < 32; i++)
            {
                var r = outbmp.Palette.Entries[i].R;
                var g = outbmp.Palette.Entries[i].G;
                var b = outbmp.Palette.Entries[i].B;
                pal.paldata[(i) * 4 + 0] = outbmp.Palette.Entries[i].R;
                pal.paldata[(i) * 4 + 1] = outbmp.Palette.Entries[i].G;
                pal.paldata[(i) * 4 + 2] = outbmp.Palette.Entries[i].B;
                pal.paldata[(i) * 4 + 3] = 255;// outbmp.Palette.Entries[i].A;
                int addr = r << 16 | g << 8 | b;
                palconv[addr] = (byte)(i);
            }
            output.pals.Add(pal);

            output.width = outbmp.Width;
            output.height = outbmp.Height;
            output.pixel = new byte[output.width * output.height];
            var data = outbmp.LockBits(new System.Drawing.Rectangle(0, 0, outbmp.Width, outbmp.Height), System.Drawing.Imaging.ImageLockMode.ReadOnly, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
            unsafe
            {
                byte* ptr = (byte*)data.Scan0;
                for (var y = 0; y < srcimg.Height; y++)
                {
                    for (var x = 0; x < srcimg.Width; x++)
                    {

                        var index = ptr[y * data.Stride + x];
                        var alpha = outbmp.Palette.Entries[index].A;
                        var c = (alpha & 0xE0) | (index & 0x1F);
                        //if (index == 255) index = 0;
                        //else
                        //    index++;
                        output.pixel[(y * srcimg.Width + x)] = (byte)c;

                    }
                }
            }
        }
        private static Dictionary<int, byte> GenPal(StbImageSharp.ImageResult srcimg, TTIMGFormat format, StbImageSharp.ImageResult palimg, ImgData img)
        {
            Dictionary<int, byte> palconv = new Dictionary<int, byte>();
            if (palimg != null)
            {
                if (format == TTIMGFormat.PAL8)
                {
                    //read pal
                    for (var i = 0; i < palimg.Height; i++)
                    {
                        var pal = new PalData();
                        pal.colorcount = 255;
                        pal.paldata = new byte[256 * 4];
                        for (var j = 1; j < 256; j++)
                        {
                            var r = palimg.Data[(i * palimg.Width + j) * 3 + 0];
                            var g = palimg.Data[(i * palimg.Width + j) * 3 + 1];
                            var b = palimg.Data[(i * palimg.Width + j) * 3 + 2];
                            pal.paldata[j * 4 + 0] = r;
                            pal.paldata[j * 4 + 1] = g;
                            pal.paldata[j * 4 + 2] = b;
                            pal.paldata[j * 4 + 3] = 255;
                            if (i == 0)
                            {
                                int addr = r << 16 | g << 8 | b;
                                palconv[addr] = (byte)j;
                            }
                        }
                        img.pals.Add(pal);
                    }

                }
                else if (format == TTIMGFormat.P5A3)
                {
                    //read pal
                    for (var i = 0; i < palimg.Height; i++)
                    {
                        var pal = new PalData();
                        pal.colorcount = 32;
                        pal.paldata = new byte[32 * 4];
                        for (var j = 0; j < 32; j++)
                        {
                            var r = palimg.Data[(i * palimg.Width + j) * 3 + 0];
                            var g = palimg.Data[(i * palimg.Width + j) * 3 + 1];
                            var b = palimg.Data[(i * palimg.Width + j) * 3 + 2];
                            pal.paldata[j * 4 + 0] = r;
                            pal.paldata[j * 4 + 1] = g;
                            pal.paldata[j * 4 + 2] = b;
                            pal.paldata[j * 4 + 3] = 255;
                            if (i == 0)
                            {
                                int addr = r << 16 | g << 8 | b;
                                palconv[addr] = (byte)j;
                            }
                        }
                        img.pals.Add(pal);
                    }
                }
            }
            else
            {

                Console.WriteLine("需要自动产生调色板，这种模式下需要图片总发色数低。");
                Console.WriteLine("Todo:自动处理调色板");
                //auto gen pal.
                if (format == TTIMGFormat.PAL8)
                {
                    PalData pal = new PalData();
                    pal.colorcount = 255;
                    pal.paldata = new byte[256 * 4];
                    int start = 1;

                    for (var y = 0; y < img.height; y++)
                    {
                        for (var x = 0; x < img.width; x++)
                        {
                            var r = srcimg.Data[(y * img.width + x) * 4 + 0];
                            var g = srcimg.Data[(y * img.width + x) * 4 + 1];
                            var b = srcimg.Data[(y * img.width + x) * 4 + 2];
                            var a = srcimg.Data[(y * img.width + x) * 4 + 3];
                            if (a < 128)
                            {
                                continue;
                            }
                            else
                            {
                                int addr = r << 16 | g << 8 | b;
                                if (palconv.ContainsKey(addr))
                                {
                                    continue;
                                }
                                pal.paldata[start * 4 + 0] = r;
                                pal.paldata[start * 4 + 1] = g;
                                pal.paldata[start * 4 + 2] = b;
                                pal.paldata[start * 4 + 3] = 255;

                                palconv[addr] = (byte)start;
                                start++;
                                if (start > 255)
                                {
                                    throw new Exception("too much colors in img. can't gen PAL8.");
                                }


                            }
                        }
                    }


                    img.pals.Add(pal);
                }
                if (format == TTIMGFormat.P5A3)
                {
                    PalData pal = new PalData();
                    pal.colorcount = 32;
                    pal.paldata = new byte[32 * 4];
                    int start = 0;
                    for (var y = 0; y < img.height; y++)
                    {
                        for (var x = 0; x < img.width; x++)
                        {
                            var r = srcimg.Data[(y * img.width + x) * 4 + 0];
                            var g = srcimg.Data[(y * img.width + x) * 4 + 1];
                            var b = srcimg.Data[(y * img.width + x) * 4 + 2];
                            {
                                int addr = r << 16 | g << 8 | b;
                                if (palconv.ContainsKey(addr))
                                {
                                    continue;
                                }

                                pal.paldata[start * 4 + 0] = r;
                                pal.paldata[start * 4 + 1] = g;
                                pal.paldata[start * 4 + 2] = b;
                                pal.paldata[start * 4 + 3] = 255;

                                palconv[addr] = (byte)start;
                                start++;
                                if (start > 32)
                                    throw new Exception("too much colors in img. can't gen P5A3.");

                            }
                        }
                    }
                    img.pals.Add(pal);
                }
            }
            return palconv;

        }
    }
}
